// SPDX-FileCopyrightText: 2023 CitruS Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

package com.fanwj.citrus.utils

import android.os.RemoteException
import com.fanwj.citrus.Backend
import com.fanwj.citrus.model.GameDir
import com.fanwj.citrus.overlay.model.OverlayControlData

import com.fanwj.citrus.features.input.model.PlayerInput

object NativeConfig {
    /**
     * Loads global config.
     */
    @Synchronized
    fun initializeGlobalConfig() {
        try {
            Backend.stub.NativeConfig_initializeGlobalConfig()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Destroys the stored global config object. This does not save the existing config.
     */
    @Synchronized
    fun unloadGlobalConfig() {
        try {
            Backend.stub.NativeConfig_unloadGlobalConfig()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Reads values in the global config file and saves them.
     */
    @Synchronized
    fun reloadGlobalConfig() {
        try {
            Backend.stub.NativeConfig_reloadGlobalConfig()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Saves global settings values in memory to disk.
     */
    @Synchronized
    fun saveGlobalConfig() {
        try {
            Backend.stub.NativeConfig_saveGlobalConfig()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Creates per-game config for the specified parameters. Must be unloaded once per-game config
     * is closed with [unloadPerGameConfig]. All switchable values that [NativeConfig] gets/sets
     * will follow the per-game config until the global config is reloaded.
     *
     * @param programId String representation of the u64 programId
     * @param fileName Filename of the game, including its extension
     */
    @Synchronized
    fun initializePerGameConfig(programId: Long, fileName: String) {
        try {
            Backend.stub.NativeConfig_initializePerGameConfig(programId, fileName)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun isPerGameConfigLoaded(): Boolean {
        try {
            return Backend.stub.NativeConfig_isPerGameConfigLoaded()
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return false
        }
    }

    /**
     * Saves per-game settings values in memory to disk.
     */
    @Synchronized
    fun savePerGameConfig() {
        try {
            Backend.stub.NativeConfig_savePerGameConfig()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Destroys the stored per-game config object. This does not save the config.
     */
    @Synchronized
    fun unloadPerGameConfig() {
        try {
            Backend.stub.NativeConfig_unloadPerGameConfig()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getBoolean(key: String, needsGlobal: Boolean): Boolean {
        try {
            return Backend.stub.NativeConfig_getBoolean(key, needsGlobal)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return false
        }
    }

    @Synchronized
    fun setBoolean(key: String, value: Boolean) {
        try {
            Backend.stub.NativeConfig_setBoolean(key, value)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getByte(key: String, needsGlobal: Boolean): Byte {
        try {
            return Backend.stub.NativeConfig_getByte(key, needsGlobal)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return -1;
        }
    }

    @Synchronized
    fun setByte(key: String, value: Byte) {
        try {
            Backend.stub.NativeConfig_setByte(key, value)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getShort(key: String, needsGlobal: Boolean): Short {
        try {
            return Backend.stub.NativeConfig_getShort(key, needsGlobal).code.toShort()
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return -1;
        }
    }

    @Synchronized
    fun setShort(key: String, value: Short) {
        try {
            Backend.stub.NativeConfig_setShort(key, value.toInt().toChar())
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getInt(key: String, needsGlobal: Boolean): Int {
        try {
            return Backend.stub.NativeConfig_getInt(key, needsGlobal)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return -1
        }
    }

    @Synchronized
    fun setInt(key: String, value: Int) {
        try {
            Backend.stub.NativeConfig_setInt(key, value)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getFloat(key: String, needsGlobal: Boolean): Float {
        try {
            return Backend.stub.NativeConfig_getFloat(key, needsGlobal)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return -1.0f
        }
    }

    @Synchronized
    fun setFloat(key: String, value: Float) {
        try {
            Backend.stub.NativeConfig_setFloat(key, value)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getLong(key: String, needsGlobal: Boolean): Long {
        try {
            return Backend.stub.NativeConfig_getLong(key, needsGlobal)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return 0L
        }
    }

    @Synchronized
    fun setLong(key: String, value: Long) {
        try {
            Backend.stub.NativeConfig_setLong(key, value)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getString(key: String, needsGlobal: Boolean): String {
        try {
            return Backend.stub.NativeConfig_getString(key, needsGlobal)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return ""
        }
    }

    @Synchronized
    fun setString(key: String, value: String) {
        try {
            Backend.stub.NativeConfig_setString(key, value)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    fun getIsRuntimeModifiable(key: String): Boolean {
        try {
            return Backend.stub.NativeConfig_getIsRuntimeModifiable(key)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return false
        }
    }

    fun getPairedSettingKey(key: String): String {
        try {
            return Backend.stub.NativeConfig_getPairedSettingKey(key)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return ""
        }
    }

    fun getIsSwitchable(key: String): Boolean {
        try {
            return Backend.stub.NativeConfig_getIsSwitchable(key)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return false
        }
    }

    @Synchronized
    fun usingGlobal(key: String): Boolean {
        try {
            return Backend.stub.NativeConfig_usingGlobal(key)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return false
        }
    }

    @Synchronized
    fun setGlobal(key: String, global: Boolean) {
        try {
            Backend.stub.NativeConfig_setGlobal(key, global)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    fun getIsSaveable(key: String): Boolean {
        try {
            return Backend.stub.NativeConfig_getIsSaveable(key)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return false
        }
    }

    fun getDefaultToString(key: String): String {
        try {
            return Backend.stub.NativeConfig_getDefaultToString(key)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return ""
        }
    }

    /**
     * Gets every [GameDir] in AndroidSettings::values.game_dirs
     */
    @Synchronized
    fun getGameDirs(): Array<GameDir> {
        try {
            return Backend.stub.NativeConfig_getGameDirs()
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return emptyArray<GameDir>()
        }
    }

    /**
     * Clears the AndroidSettings::values.game_dirs array and replaces them with the provided array
     */
    @Synchronized
    fun setGameDirs(dirs: Array<GameDir>) {
        try {
            Backend.stub.NativeConfig_setGameDirs(dirs)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Adds a single [GameDir] to the AndroidSettings::values.game_dirs array
     */
    @Synchronized
    fun addGameDir(dir: GameDir) {
        try {
            Backend.stub.NativeConfig_addGameDir(dir)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Gets an array of the addons that are disabled for a given game
     *
     * @param programId String representation of a game's program ID
     * @return An array of disabled addons
     */
    @Synchronized
    fun getDisabledAddons(programId: Long): Array<String> {
        try {
            return Backend.stub.NativeConfig_getDisabledAddons(programId)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return emptyArray<String>()
        }
    }

    /**
     * Clears the disabled addons array corresponding to [programId] and replaces them
     * with [disabledAddons]
     *
     * @param programId String representation of a game's program ID
     * @param disabledAddons Replacement array of disabled addons
     */
    @Synchronized
    fun setDisabledAddons(programId: Long, disabledAddons: Array<String>) {
        try {
            Backend.stub.NativeConfig_setDisabledAddons(programId, disabledAddons)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Gets an array of [OverlayControlData] from settings
     *
     * @return An array of [OverlayControlData]
     */
    @Synchronized
    fun getOverlayControlData(): Array<OverlayControlData> {
        try {
            return Backend.stub.NativeConfig_getOverlayControlData()
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return emptyArray<OverlayControlData>()
        }
    }

    /**
     * Clears the AndroidSettings::values.overlay_control_data array and replaces its values
     * with [overlayControlData]
     *
     * @param overlayControlData Replacement array of [OverlayControlData]
     */
    @Synchronized
    fun setOverlayControlData(overlayControlData: Array<OverlayControlData>) {
        try {
            Backend.stub.NativeConfig_setOverlayControlData(overlayControlData)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    @Synchronized
    fun getInputSettings(global: Boolean): Array<PlayerInput> {
        try {
            return Backend.stub.NativeConfig_getInputSettings(global)
        } catch (e: RemoteException) {
            Backend.handleException(e)
            return emptyArray<PlayerInput>()
        }
    }

    @Synchronized
    fun setInputSettings(value: Array<PlayerInput>, global: Boolean) {
        try {
            Backend.stub.NativeConfig_setInputSettings(value, global)
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }

    /**
     * Saves control values for a specific player
     * Must be used when per game config is loaded
     */
    @Synchronized
    fun saveControlPlayerValues() {
        try {
            Backend.stub.NativeConfig_saveControlPlayerValues()
        } catch (e: RemoteException) {
            Backend.handleException(e)
        }
    }
}
